/****************************************************************************
 * Copyright (c) 2010-2011 Composent, Inc. and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *   Composent, Inc. - initial API and implementation
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package org.eclipse.ecf.osgi.services.remoteserviceadmin;

import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.eclipse.ecf.core.identity.IDFactory;
import org.eclipse.ecf.core.identity.Namespace;
import org.eclipse.ecf.discovery.IDiscoveryAdvertiser;
import org.eclipse.ecf.discovery.IServiceInfo;
import org.eclipse.ecf.discovery.IServiceProperties;
import org.eclipse.ecf.discovery.ServiceInfo;
import org.eclipse.ecf.discovery.ServiceProperties;
import org.eclipse.ecf.discovery.identity.IServiceTypeID;
import org.eclipse.ecf.discovery.identity.ServiceIDFactory;
import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.PropertiesUtil;

/**
 * Default implementation of {@link IServiceInfoFactory}.
 * 
 */
public class ServiceInfoFactory extends AbstractMetadataFactory implements
		IServiceInfoFactory {

	private final List<String> discoveryProperties;
	
	public ServiceInfoFactory() {
		discoveryProperties = Arrays.asList(new String[] {
				RemoteConstants.DISCOVERY_DEFAULT_SERVICE_NAME_PREFIX,
				RemoteConstants.DISCOVERY_NAMING_AUTHORITY,
				RemoteConstants.DISCOVERY_PROTOCOLS,
				RemoteConstants.DISCOVERY_SCOPE,
				RemoteConstants.DISCOVERY_SERVICE_NAME,
				RemoteConstants.DISCOVERY_SERVICE_PRIORITY,
				RemoteConstants.DISCOVERY_SERVICE_TTL,
				RemoteConstants.DISCOVERY_SERVICE_TYPE,
				RemoteConstants.DISCOVERY_SERVICE_WEIGHT
		});
	}
	/**
	 * @since 3.0
	 */
	public IServiceInfo createServiceInfo(
			IDiscoveryAdvertiser advertiser,
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription) {
		try {
				IServiceTypeID serviceTypeID = createServiceTypeID(
						endpointDescription, advertiser);
				String serviceName = createServiceName(endpointDescription,
						advertiser, serviceTypeID);
				URI uri = createURI(endpointDescription, advertiser,
						serviceTypeID, serviceName);
				IServiceProperties serviceProperties = createServiceProperties(
						endpointDescription, advertiser, serviceTypeID,
						serviceName, uri);
				
				Map edProperties = endpointDescription.getProperties();
				int priority = PropertiesUtil.getIntWithDefault(edProperties, RemoteConstants.DISCOVERY_SERVICE_PRIORITY, ServiceInfo.DEFAULT_PRIORITY);
				int weight = PropertiesUtil.getIntWithDefault(edProperties, RemoteConstants.DISCOVERY_SERVICE_WEIGHT,ServiceInfo.DEFAULT_WEIGHT);
				Long ttl = PropertiesUtil.getLongWithDefault(edProperties, RemoteConstants.DISCOVERY_SERVICE_TTL, ServiceInfo.DEFAULT_TTL);
				return new ServiceInfo(uri, serviceName, serviceTypeID,
						priority, weight, serviceProperties, ttl);
		} catch (Exception e) {
			logError(
					"createServiceInfo", //$NON-NLS-1$
					"Exception creating service info for endpointDescription=" //$NON-NLS-1$
							+ endpointDescription + ",advertiser=" + advertiser, //$NON-NLS-1$
					e);
			return null;
		}
	}

	protected IServiceInfo createServiceInfo(URI uri, String serviceName,
			IServiceTypeID serviceTypeID, IServiceProperties serviceProperties) {
		return new ServiceInfo(uri, serviceName, serviceTypeID,
				serviceProperties);
	}

	/**
	 * @param endpointDescription endpoint description
	 * @param advertiser advertiser
	 * @param serviceTypeID serviceTypeID
	 * @param serviceName serviceName
	 * @param uri uri
	 * @return IServiceProperties for the given input parameters
	 * @since 3.0
	 */
	protected IServiceProperties createServiceProperties(
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
			IDiscoveryAdvertiser advertiser, IServiceTypeID serviceTypeID,
			String serviceName, URI uri) {
		Map<String,Object> props = endpointDescription.getProperties();
		Map<String,Object> result = new TreeMap<String,Object>(String.CASE_INSENSITIVE_ORDER);
		for(String key: props.keySet())
			if (!discoveryProperties.contains(key))
				result.put(key, props.get(key));
		
		ServiceProperties spResult = new ServiceProperties();
		encodeServiceProperties(new EndpointDescription(result), spResult);
		return spResult;
	}

	/**
	 * @param endpointDescription endpoint description
	 * @param advertiser advertiser
	 * @param serviceTypeID serviceTypeID
	 * @param serviceName service name
	 * @return URI created
	 * @throws URISyntaxException thrown if URI cannot be created
	 * @since 3.0
	 */
	protected URI createURI(
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
			IDiscoveryAdvertiser advertiser, IServiceTypeID serviceTypeID,
			String serviceName) throws URISyntaxException {
		String path = "/" + serviceName; //$NON-NLS-1$
		String str = endpointDescription.getId();
		URI uri = null;
		while (true) {
			try {
				uri = new URI(str);
				if (uri.getHost() != null) {
					break;
				} else {
					final String rawSchemeSpecificPart = uri
							.getRawSchemeSpecificPart();
					// make sure we break eventually
					if (str.equals(rawSchemeSpecificPart)) {
						uri = null;
						break;
					} else {
						str = rawSchemeSpecificPart;
					}
				}
			} catch (URISyntaxException e) {
				uri = null;
				break;
			}
		}
		String scheme = RemoteConstants.DISCOVERY_SERVICE_TYPE;
		int port = 32565;
		if (uri != null) {
			port = uri.getPort();
			if (port == -1)
				port = 32565;
		}
		String host = null;
		if (uri != null) {
			host = uri.getHost();
		} else {
			try {
				host = InetAddress.getLocalHost().getHostAddress();
			} catch (Exception e) {
				logWarning(
						"createURI", //$NON-NLS-1$
						"failed to get local host adress, falling back to \'localhost\'.", e); //$NON-NLS-1$
				host = "localhost"; //$NON-NLS-1$
			}
		}
		return new URI(scheme, null, host, port, path, null, null);
	}

	/**
	 * @param endpointDescription endpoint description
	 * @param advertiser advertiser
	 * @param serviceTypeID serviceTypeID
	 * @return String created service name for input parameters
	 * @since 3.0
	 */
	protected String createServiceName(
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
			IDiscoveryAdvertiser advertiser, IServiceTypeID serviceTypeID) {
		// First create unique default name
		String defaultServiceName = createDefaultServiceName(
				endpointDescription, advertiser, serviceTypeID);
		// Look for service name that was explicitly set
		String serviceName = PropertiesUtil.getStringWithDefault(
				endpointDescription.getProperties(),
				RemoteConstants.DISCOVERY_SERVICE_NAME, defaultServiceName);
		return serviceName;
	}

	/**
	 * @param endpointDescription endpoint description
	 * @param advertiser advertiser
	 * @param serviceTypeID serviceTypeID
	 * @return String default service name created
	 * @since 3.0
	 */
	protected String createDefaultServiceName(
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
			IDiscoveryAdvertiser advertiser, IServiceTypeID serviceTypeID) {
		return RemoteConstants.DISCOVERY_DEFAULT_SERVICE_NAME_PREFIX
				+ IDFactory.getDefault().createGUID().getName();
	}

	/**
	 * @param endpointDescription endpoint description
	 * @param advertiser advertiser
	 * @return IServiceTypeID created service type ID
	 * @since 3.0
	 */
	protected IServiceTypeID createServiceTypeID(
			org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
			IDiscoveryAdvertiser advertiser) {
		
		Namespace servicesNamespace = IDFactory.getDefault()
				.getNamespaceByName("ecf.namespace.discovery"); //$NON-NLS-1$
		if (advertiser != null) {
			servicesNamespace = advertiser.getServicesNamespace();
		}

		Map props = endpointDescription.getProperties();
		String[] scopes = PropertiesUtil.getStringArrayWithDefault(props,
				RemoteConstants.DISCOVERY_SCOPE, IServiceTypeID.DEFAULT_SCOPE);
		String[] protocols = PropertiesUtil.getStringArrayWithDefault(props,
				RemoteConstants.DISCOVERY_PROTOCOLS,
				IServiceTypeID.DEFAULT_SCOPE);
		String namingAuthority = PropertiesUtil.getStringWithDefault(props,
				RemoteConstants.DISCOVERY_NAMING_AUTHORITY,
				IServiceTypeID.DEFAULT_NA);
		return ServiceIDFactory.getDefault().createServiceTypeID(
				servicesNamespace,
				new String[] { RemoteConstants.DISCOVERY_SERVICE_TYPE },
				scopes, protocols, namingAuthority);
	}
}
